package main

import (
	"fmt"
	"hash/crc32"
	"math/rand"
	"sync"
	"time"
)

var Null struct{} = struct{}{}

type Item struct {
	Locker sync.RWMutex
	Data   map[interface{}]bool
}

func NewItem() *Item {

	item := &Item{}
	(*item).Locker = sync.RWMutex{}
	(*item).Data = make(map[interface{}]bool, 10)
	return item
}

type Set struct {
	Items []*Item
}

//传入一个int 表示我这个 集合包含多少个map（并发的时候插入的map的数量）
func NewSet(num int) *Set {
	set := &Set{}
	(*set).Items = make([]*Item, num)

	for i := 0; i < num; i++ {
		set.Items[i] = NewItem()
	}
	fmt.Println("new set has ", num, " queues initialized ")
	return set
}

//输入哈希并取模 得到应该在哪个通道插入 核心代码
func (set *Set) FromHashGetMod(hash uint32) int {
	return int(hash) % len(set.Items)

}

//对数据做哈希 输入函数FromHashGetMod 返回 插入位置 这个是个核心代码
func (set *Set) Findplace(data interface{}) int {
	b := fmt.Sprintln(data)
	hash := crc32.ChecksumIEEE([]byte(b))
	place := set.FromHashGetMod(hash)
	//fmt.Println(hash)
	return place
}
func (set *Set) Add(data interface{}) int {
	place := set.Findplace(data)
	//fmt.Println(place)
	//lock
	set.Items[place].Locker.Lock()
	set.Items[place].Data[data] = true
	set.Items[place].Locker.Unlock()

	//想办法实现将存入的数据 哈希之后放入items数组里面
	fmt.Println("data: ", data, "is in quere: ", place)
	return place
}
func (set *Set) Check(data interface{}) bool {
	place := set.Findplace(data)

	//加载读锁
	set.Items[place].Locker.RLock()
	//卸载读锁
	defer set.Items[place].Locker.RUnlock()
	if set.Items[place].Data[data] == true {
		fmt.Println("data", data, " exsits in queue: ", place)
		return true
	} else {
		fmt.Println("data ", data, "is not existing ")
		return false
	}

}
func (set *Set) Delete(data interface{}) {
	place := set.Findplace(data)
	if set.Check(data) {
		set.Items[place].Locker.Lock()
		delete(set.Items[place].Data, data)
		set.Items[place].Locker.Unlock()
	}
	//fmt.Println(place)

}

//这里有个很费解的问题 无论Num设置多少程序执行的时间的都是固定的，感觉瓶颈不在锁。。。 跟我的预期不符呀
const Num int = 64

func main() {
	var wg sync.WaitGroup
	set := NewSet(Num)
	rand.Seed(time.Now().Unix())
	//3个例程各执行1000次 总共写了1000 * 10000 * 2 次 读了1000 *10000 *2
	for j := 0; j <= 1000; j++ {
		wg.Add(3)
		go func() {
			//写一万次0到1000的随机数进入set
			for i := 0; i <= 10000; i++ {
				set.Add(rand.Intn(1000))
			}
			wg.Done()
		}()
		go func() {
			//读一万次 查看
			for i := 0; i <= 10000; i++ {
				set.Check(rand.Intn(1000))
			}
			wg.Done()
		}()
		go func() {
			//删除一万次
			for i := 0; i <= 10000; i++ {
				set.Delete(rand.Intn(1000))
			}
			wg.Done()
		}()
	}
	wg.Wait()
	// set.Add("testdata1")
	// set.Add("testdaafdadsfadsfsadta1")
	// set.Add(1)
	// set.Add(2)
	// set.Delete(1)
	// set.Check(1)
	// set.Check(2)
}

// [Done] exited with code=0 in 114.344 seconds 大概需要连分钟 感觉 并发效率不行呀
// 可以考虑下初始化时传入参数时怎么处理